import { defineStore, acceptHMRUpdate } from "pinia";
import { computed, onMounted, reactive, ref } from "vue";
import { FopApi, type MapperConfig } from "@/lib/api";
import { type AvailableMapper, asAvailableMapper } from "@/lib/mapper";
import { DefaultMapper } from "@/lib/constants";
import { getBaseName } from "@/lib/utils";

const useMapperStore = defineStore("mapper", () => {
    const name = ref("");
    const mapper = ref<AvailableMapper>(DefaultMapper);
    const all = reactive<string[]>([]);
    const set = reactive<Set<string>>(new Set());

    /**
     * 加载映射器配置
     */
    onMounted(load);

    /**
     * 检查映射器配置，转换为可用映射器
     * @param mapper 映射器配置对象
     * @throws 转换失败则抛出错误
     */
    function checkMapper(mapper: MapperConfig): AvailableMapper {
        const res = asAvailableMapper(mapper);
        if (res !== undefined) {
            return res;
        } else {
            throw new Error("不可用的映射器配置");
        }
    }

    /**
     * 设置当前映射器的名称
     * @param value 新名称
     * @returns 是否设置成功
     */
    async function setName(value: string): Promise<boolean> {
        if (name.value === value) {
            return true;
        }

        if (value === "") {
            await FopApi.writeJson("mapper", value);
            mapper.value = DefaultMapper;
            name.value = "";
        } else {
            if (set.has(value)) {
                try {
                    await FopApi.writeJson("mapper", value);
                    mapper.value = checkMapper(await FopApi.readJson(`mapper/${value}`));
                    name.value = value;
                    return true;
                } catch (error) {
                    console.error("读取映射器配置失败", error);
                    return false;
                }
            } else {
                console.warn("映射器不存在");
                return false;
            }
        }

        return true;
    }

    /**
     * 设置映射器配置
     * @param value 映射器配置对象
     * @returns 是否设置成功
     */
    async function setMapper(value: AvailableMapper): Promise<boolean> {
        if (name.value === "") {
            console.warn("请先设置映射器名称");
            return false;
        }

        try {
            await FopApi.writeJson(`mapper/${name.value}`, value);
            mapper.value = value;
        } catch (error) {
            console.error("写入映射器配置失败", error);
            return false;
        }

        return true;
    }

    /**
     * 异步函数，用于加载映射器配置\
     * @returns 是否成功
     */
    async function load() {
        // 读取 Mapper 列表
        const res = await FopApi.globInData("mapper/*.json");
        all.length = 0;
        set.clear();
        all.push(...res.map(getBaseName));
        all.forEach(set.add.bind(set));

        // 读取当前项
        let value: string;
        try {
            value = await FopApi.readJson<string>("mapper");
        } catch (error) {
            console.warn("读取映射器名称失败", error);
            return;
        }

        // 读取 name 和 mapper
        if (set.has(value)) {
            try {
                mapper.value = checkMapper(await FopApi.readJson<MapperConfig>(`mapper/${value}`));
                name.value = value;
            } catch (error) {
                console.error("读取映射器配置失败", error);
                mapper.value = DefaultMapper;
                name.value = "";
            }
        } else {
            await FopApi.removeJson("mapper");
        }
    }

    /**
     * 添加映射器配置
     * @param key 映射器名称
     * @param value 映射器配置
     * @returns 是否添加成功
     */
    async function add(key: string, value: MapperConfig): Promise<boolean> {
        if (key === "") {
            console.warn("映射器名称不能为空");
            return false;
        }

        if (set.has(key)) {
            console.warn("映射器名称已存在");
            return false;
        }

        try {
            await FopApi.writeJson(`mapper/${key}`, value);
            all.push(key);
            set.add(key);
        } catch (error) {
            console.error("添加映射器", key, "失败", error);
            return false;
        }

        return true;
    }

    /**
     * 移除指定键的映射
     * @param key 要移除的键
     * @returns 是否移除成功
     */
    async function remove(key: string): Promise<boolean> {
        // 验证参数
        if (key === "") {
            console.warn("映射器名称不能为空");
            return false;
        }

        // 验证当前项
        if (name.value === key) {
            name.value = "";
            mapper.value = DefaultMapper;
        }

        // 移除
        try {
            await FopApi.removeJson(`mapper/${key}`);
            all.splice(all.indexOf(key), 1);
            set.delete(key);
        } catch (error) {
            console.error("移除映射器", key, "失败", error);
            return false;
        }

        return true;
    }

    /**
     * 重命名映射器
     * @param key 当前名称
     * @param newName 新名称
     * @returns 是否重命名成功
     */
    async function rename(key: string, newName: string): Promise<boolean> {
        // 验证当前项
        if (key === "") {
            console.warn("不能重命名默认映射器");
            return false;
        }

        // 验证参数
        if (newName === "") {
            console.warn("映射器名称不能为空");
            return false;
        }

        // 验证是否重命名到当前项
        if (key === newName) {
            return true;
        }

        // 验证是否重命名到已存在的映射器
        if (set.has(newName)) {
            console.warn("映射器名称已存在");
            return false;
        }

        // 重命名
        try {
            await FopApi.renameJson(`mapper/${key}`, `mapper/${newName}`);
            all.splice(all.indexOf(key), 1, newName);
            set.delete(key);
            set.add(newName);
            if (name.value === key) {
                name.value = newName;
            }
        } catch (error) {
            console.error("重命名映射器", key, "失败", error);
            return false;
        }

        return true;
    }

    return {
        name: computed(() => name.value),
        now: computed(() => mapper.value),
        all: computed(() => all),
        set: computed(() => set),
        setName,
        setMapper,
        load,
        add,
        remove,
        rename,
    };
});

if (import.meta.hot) {
    import.meta.hot.accept(acceptHMRUpdate(useMapperStore, import.meta.hot));
}

export default useMapperStore;
